#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "ping/ping_sock.h"
#include "ping_func.h"
#include "utils_log.h"

#define PING_LOSS_COUNT   5      //ping丢包率，即5%
#define EXEC_PING_SUCCESS (0)    //ping执行成功
#define EXEC_PING_FAIL    (-1)   //ping执行失败
#define MAX_WAIT_COUNT    10

static int g_result = NETWORK_CONNECTIVITY_FAIL;
static int g_execStatus = EXEC_PING_FAIL;

static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
{
    uint8_t ttl;
    uint16_t seqno;
    uint32_t elapsed_time, recv_len;
    ip_addr_t target_addr;
    esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
    esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
    esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
    esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
    esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
    printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
           recv_len, ipaddr_ntoa((ip_addr_t*)&target_addr), seqno, ttl, elapsed_time);
}

static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
    uint16_t seqno;
    ip_addr_t target_addr;
    esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
    esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
    printf("From %s icmp_seq=%d timeout\n",ipaddr_ntoa((ip_addr_t*)&target_addr), seqno);
}

static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
    ip_addr_t target_addr;
    uint32_t transmitted;
    uint32_t received;
    uint32_t total_time_ms;
    esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
    esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
    esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
    esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
    uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
    printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
    printf("%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
           transmitted, received, loss, total_time_ms);
    if (loss < PING_LOSS_COUNT){
        g_result = NETWORK_CONNECTIVITY_OK;
    } else {
        g_result = NETWORK_CONNECTIVITY_FAIL;
    }
    g_execStatus = EXEC_PING_SUCCESS;
  
    esp_ping_stop(hdl);
    esp_ping_delete_session(hdl);
}

int ExecPingCmd(PingConfig_t *pingCfg)
{
    if (NULL == pingCfg){
        printf("ping: pingCfg is invalid\r\n");
        LOG_TO_FILE("ping: pingCfg is invalid");
        return -1;
    }
    esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
    config.task_stack_size = 0x1000;
    
    if(pingCfg->count > 0){
       config.count = pingCfg->count;
    }
    if(pingCfg->intervalMs > 0){
        config.interval_ms = pingCfg->intervalMs;
    }
    if(pingCfg->timeoutMs > 0){
        config.timeout_ms = pingCfg->timeoutMs;
    }
    printf("ping: timeout_ms is %d\r\n", config.timeout_ms);
    
    if(pingCfg->dataSize > 0){
        config.data_size = pingCfg->dataSize;
    }
    if(pingCfg->tos > 0){
        config.tos = pingCfg->tos;
    }
    
    ip_addr_t targetAddr;
    memset(&targetAddr, 0, sizeof(targetAddr));
    inet_aton(pingCfg->targetAddr, &targetAddr);
    config.target_addr = targetAddr;
    printf("ping: target addr %s\n", pingCfg->targetAddr);
    esp_ping_callbacks_t cbs = {
        .on_ping_success = cmd_ping_on_ping_success,
        .on_ping_timeout = cmd_ping_on_ping_timeout,
        .on_ping_end = cmd_ping_on_ping_end,
        .cb_args = NULL
    };
    esp_ping_handle_t pingHandle;
    esp_err_t ret = esp_ping_new_session(&config, &cbs, &pingHandle);
    if (ret != ESP_OK){
        printf("esp ping new session failed:%s\n", esp_err_to_name(ret));
        LOG_TO_FILE("esp ping new session failed:%s", esp_err_to_name(ret));
        return -1;
    }
    
    ret = esp_ping_start(pingHandle);
    if (ret != ESP_OK){
        printf("esp ping start failed:%s\n", esp_err_to_name(ret));
        LOG_TO_FILE("esp ping start failed:%s", esp_err_to_name(ret));
        return -1;
    }
    g_result = NETWORK_CONNECTIVITY_FAIL;
    g_execStatus = EXEC_PING_FAIL;
    return 0;
}

int GetNetworkConnectivityResult()
{
    int waitCount = 0;
    while(g_execStatus == EXEC_PING_FAIL){
        waitCount++;
       
        if( waitCount == MAX_WAIT_COUNT) { break; }
        sleep(1);
    }
    
    return g_result;
}
